package com.isyscore.os.metadata.manager;

import com.isyscore.os.core.exception.DataFactoryException;
import com.isyscore.os.core.exception.ErrorCode;
import com.isyscore.os.metadata.database.AbstractDatabase;
import com.isyscore.os.metadata.enums.DataSourceTypeEnum;
import com.isyscore.os.metadata.model.dto.DataSourceDTO;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.dbutils.DbUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * @author fjZheng
 * @version 1.0
 * @date 2020/11/20 14:14
 */
@Component
@Slf4j
public class DataSourceManager {

    /**
     * 5分钟过期
     */
//    private final Cache<String, DataSource> datasourceMd5Map = Caffeine.newBuilder()
//            .maximumSize(3)
//            .expireAfterAccess(5, TimeUnit.MINUTES)
//            .removalListener((key, value, cause) -> {
//                // 过期移除datasource时需要显式关闭pool.
//                if (Objects.nonNull(value)) {
//                    ((HikariDataSource) value).close();
//                    System.out.println(key+":"+value+"清除完成");
//                }
//            }).build();

    private final ConcurrentHashMap<String,DataSource> datasourceMd5Map = new ConcurrentHashMap();

    public DataSource getDataSource(DataSourceDTO datasourceDTO) throws SQLException {
        String driverClassName = DataSourceTypeEnum.getType(datasourceDTO.getType()).getDriverClass();

        String jdbcUrl = getJdbcUrl(datasourceDTO);

        String md5Str = jdbcUrl + datasourceDTO.getUserName() + datasourceDTO.getPlaintextPassword();
        String md5 = DigestUtils.md5DigestAsHex(md5Str.getBytes());
        if(datasourceMd5Map.containsKey(md5)){
            DataSource dataSource = datasourceMd5Map.get(md5);
            return  dataSource;
        }
        final HikariConfig config = new HikariConfig();
        config.setConnectionTimeout(SECONDS.toMillis(10));
        config.setUsername(datasourceDTO.getUserName());
        config.setPassword(datasourceDTO.getPlaintextPassword());
        config.setDriverClassName(driverClassName);
        config.setJdbcUrl(jdbcUrl);
        config.setMaximumPoolSize(5);
        config.setMaxLifetime(60*1000);
        if(DataSourceTypeEnum.getType(datasourceDTO.getType()) == DataSourceTypeEnum.ORACLE || DataSourceTypeEnum.getType(datasourceDTO.getType()) == DataSourceTypeEnum.DM){
            config.setConnectionTestQuery("select 1 from dual");
        }else{
            config.setConnectionTestQuery("select 1");
        }
        final HikariDataSource newDataSource = new HikariDataSource(config);
        datasourceMd5Map.put(md5, newDataSource);
        return newDataSource;
    }

    public void removeDataSource(DataSourceDTO datasourceDTO){
        String jdbcUrl = getJdbcUrl(datasourceDTO);
        String md5Str = jdbcUrl + datasourceDTO.getUserName() + datasourceDTO.getPlaintextPassword();
        String md5 = DigestUtils.md5DigestAsHex(md5Str.getBytes());
        if(datasourceMd5Map.containsKey(md5)){
            HikariDataSource dataSource = (HikariDataSource)datasourceMd5Map.remove(md5);
            dataSource.close();
        }
    }

    private String getJdbcUrl(DataSourceDTO datasourceDTO) {
        AbstractDatabase db = DatabaseManager.findDb(datasourceDTO);

        return db.jdbcUrl(datasourceDTO.getIp(),
                            Integer.parseInt(datasourceDTO.getPort()),
                            datasourceDTO.getSelectDatabaseName(),
                            datasourceDTO.getBasicType(),
                            datasourceDTO.getBasicValue());

//        String jdbcUrl;
//        if (Objects.equals(datasourceDTO.getType(), DataSourceTypeEnum.ORACLE.getCode())) {
//            //oracle,需要判断basic下的 sid 还是 servicename
//            jdbcUrl = String.format(DataSourceTypeEnum.getUrlTemplate(datasourceDTO.getType(), datasourceDTO.getBasicType()),
//                    datasourceDTO.getIp(),
//                    datasourceDTO.getPort(),
//                    datasourceDTO.getBasicValue());
//        } else if (Objects.equals(datasourceDTO.getType(), CommonConstant.SQLSERVER)) {
//            jdbcUrl = String.format(DataSourceTypeEnum.getUrlTemplate(datasourceDTO.getType(), null),
//                    datasourceDTO.getIp(),
//                    datasourceDTO.getPort(),
//                    UdmpUtils.getDatabaseName(datasourceDTO.getSelectDatabaseName()));
//        } else {
//            jdbcUrl = String.format(DataSourceTypeEnum.getUrlTemplate(datasourceDTO.getType(), null),
//                    datasourceDTO.getIp(),
//                    datasourceDTO.getPort(),
//                    datasourceDTO.getSelectDatabaseName());
//        }
//        return jdbcUrl;
    }

    /**
     * 获取连接
     *
     * @param datasourceDTO
     * @return
     */
    public Connection getConn(DataSourceDTO datasourceDTO) {
        try {
            DataSource dataSource = getDataSource(datasourceDTO);
            if (null == dataSource) {
                return null;
            }
            Connection con = dataSource.getConnection();
//            if (Objects.equals(datasourceDTO.getType(), CommonConstant.ORACLE)) {
//                Statement statement = con.createStatement();
//                statement.execute(String.format(
//                        "ALTER SESSION SET CURRENT_SCHEMA= \"%s\"",
//                        datasourceDTO.getSelectDatabaseName()));
//            }
            return con;
        } catch (SQLException e) {
            log.error("data source connection error", e);
            throw new DataFactoryException(ErrorCode.DATA_SOURCE_CONNECTION_ERROR);
        }
    }

    /**
     * 关闭连接
     *
     * @param conn
     * @param ps
     * @param rs
     * @Author fjzheng
     */
    public  void close(Connection conn, Statement ps, ResultSet rs) {
        DbUtils.closeQuietly(conn, ps, rs);
    }


}
